﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Text;
using Ujimanet.ISO_IEC_18004_2000;

namespace DrawQR
{
    enum Axis { X, Y };
    class Program
    {

        private static Bitmap _objBitmap;
        private static Graphics _objGraphics;
        private static QR _qr;
        private static ArrayList _collisionList;




        private static void drawPositionDetectionPattern(int x, int y)
        {
            Rectangle quietZone = new Rectangle(x - 1, y - 1, 8, 8);
            Rectangle pos7 = new Rectangle(x, y, 6, 6);
            Rectangle pos5 = new Rectangle(x + 1, y + 1, 4, 4);
            Rectangle pos3 = new Rectangle(x + 2, y + 2, 3, 3);

            Pen p = new Pen(Color.Black);
            Pen p2 = new Pen(Color.White);
            p.Width = 1;
            Brush b1 = new SolidBrush(Color.Black);
            _objGraphics.DrawRectangle(p2, quietZone);
            _objGraphics.DrawRectangle(p, pos7);
            _objGraphics.DrawRectangle(p2, pos5);
            _objGraphics.FillRectangle(b1, pos3);



        }
        private static void drawAlignmentPattern(int x, int y)
        {
            Rectangle pos5 = new Rectangle(x - 2, y - 2, 4, 4);
            Rectangle pos3 = new Rectangle(x - 1, y - 1, 2, 2);
            Rectangle pos1_cent = new Rectangle(x, y, 1, 1);

            Pen p = new Pen(Color.Black);
            Pen p2 = new Pen(Color.White);
            p.Width = 1;
            Brush b1 = new SolidBrush(Color.Black);

            _objGraphics.DrawRectangle(p, pos5);
            _objGraphics.DrawRectangle(p2, pos3);
            _objGraphics.FillRectangle(b1, pos1_cent);



        }
        private static void drawAllAlignmentPatterns(QRVersion v)
        {
            switch (v)
            {
                case QRVersion.Version1:
                    break;
                case QRVersion.Version2:
                    break;
                case QRVersion.Version3:
                    break;
                case QRVersion.Version4:
                    break;
                case QRVersion.Version5:
                    break;
                case QRVersion.Version6:
                    break;
                case QRVersion.Version7:
                    break;
                case QRVersion.Version8:
                    break;
                case QRVersion.Version9:
                    break;
                case QRVersion.Version10:
                    break;
                case QRVersion.Version11:
                    break;
                case QRVersion.Version12:
                    break;
                case QRVersion.Version13:
                    break;
                case QRVersion.Version14:
                    break;
                case QRVersion.Version15:
                    break;
                case QRVersion.Version16:
                    break;
                case QRVersion.Version17:
                    break;
                case QRVersion.Version18:
                    break;
                case QRVersion.Version19:
                    break;
                case QRVersion.Version20:
                    break;
                case QRVersion.Version21:
                    break;
                case QRVersion.Version22:
                    break;
                case QRVersion.Version23:
                    break;
                case QRVersion.Version24:
                    break;
                case QRVersion.Version25:
                    break;
                case QRVersion.Version26:
                    break;
                case QRVersion.Version27:
                    break;
                case QRVersion.Version28:
                    break;
                case QRVersion.Version29:
                    break;
                case QRVersion.Version30:
                    break;
                case QRVersion.Version31:
                    break;
                case QRVersion.Version32:
                    break;
                case QRVersion.Version33:
                    break;
                case QRVersion.Version34:
                    break;
                case QRVersion.Version35:
                    break;
                case QRVersion.Version36:
                    break;
                case QRVersion.Version37:
                    break;
                case QRVersion.Version38:
                    break;
                case QRVersion.Version39:
                    break;
                case QRVersion.Version40:
                    break;
            }
            //for (int i = 0; i < ((int)_qr.Version) - 16; i++)
            //{

            //        if ((i - 7) % 20 == 0 && (i - 7) >= 20 )
            //        {
            //            drawAlignmentPattern(i-1, 6);
            //        }

            //}

            //for (int j = 0; j < ((int)_qr.Version) - 16; j++)
            //{

            //    if ((j - 7) % 20 == 0 && (j - 7) >= 20)
            //    {
            //        drawAlignmentPattern(6, j-1);
            //    }

            //}

            for (int i = 6; i < ((int)v); i += 6)
            {
                for (int j = 6; j < ((int)v); j += 6)
                {
                    if ((i == 6 && j < ((int)v) - 18) ^ (j == 6 && i < ((int)v) - 18))
                        drawAlignmentPattern(i, j);

                }
            }



        }
        private static void drawTimingPattern()
        {

            for (int count = 8; count < ((int)_qr.Version) - 8; count++)
            {
                Rectangle r = new Rectangle(count, 6, 1, 1);
                Rectangle r2 = new Rectangle(6, count, 1, 1);
                Brush b2 = new SolidBrush(Color.Black);
                Brush b3 = new SolidBrush(Color.White);

                if (count % 2 == 0)
                {
                    _objGraphics.FillRectangle(b2, r);
                    _objGraphics.FillRectangle(b2, r2);
                }
                else
                {
                    _objGraphics.FillRectangle(b3, r);
                    _objGraphics.FillRectangle(b3, r2);
                }


            }

        }
        private static void colorGray()
        {
            for (int x = 20; x >= 0; x--)
            {
                for (int y = 20; y >= 0; y--)
                {

                    _objBitmap.SetPixel(x, y, Color.Gray);

                }
            }


        }
        private static void drawTimingPattern(Axis axis)
        {

            if (axis == Axis.X)
            {
                for (int count = 8; count < ((int)_qr.Version) - 8; count++)
                {
                    Rectangle r = new Rectangle(count, 6, 1, 1);

                    Brush b2 = new SolidBrush(Color.Black);
                    Brush b3 = new SolidBrush(Color.White);

                    if (count % 2 == 0)
                    {
                        _objGraphics.FillRectangle(b2, r);

                    }
                    else
                    {
                        _objGraphics.FillRectangle(b3, r);

                    }


                }
            }
            else if (axis == Axis.Y)
            {
                for (int count = 8; count < 169; count++)
                {

                    Rectangle r2 = new Rectangle(6, count, 1, 1);
                    Brush b2 = new SolidBrush(Color.Black);
                    Brush b3 = new SolidBrush(Color.White);

                    if (count % 2 == 0)
                    {

                        _objGraphics.FillRectangle(b2, r2);
                    }
                    else
                    {

                        _objGraphics.FillRectangle(b3, r2);
                    }


                }
            }
        }
        static void drawFormatInformation()
        {
            Pen p = new Pen(Color.White);
            Point p1, p2, p3;
            p1 = new Point(8, 0);
            p2 = new Point(8, 8);
            p3 = new Point(0, 8);
            _objGraphics.DrawLine(p, p1, p2);
            _objGraphics.DrawLine(p, p2, p3);

            p1 = new Point(((int)_qr.Version) - 9, 0);
            p2 = new Point(((int)_qr.Version) - 8, 8);
            p3 = new Point(((int)_qr.Version), 8);
            //_objGraphics.DrawLine(p, p1, p2);
            _objGraphics.DrawLine(p, p2, p3);

            p1 = new Point(0, ((int)_qr.Version) - 9);
            p2 = new Point(8, ((int)_qr.Version) - 8);
            p3 = new Point(8, ((int)_qr.Version));
            //_objGraphics.DrawLine(p, p1, p2);
            _objGraphics.DrawLine(p, p2, p3);
        }


        static void Main(string[] args)
        {
            _collisionList = new ArrayList();
            string encodestring = args[0];
            _qr = new QR();
            _qr.EncodingType = EncodingType.Edifact;

            _qr.Data = args[0];
            _qr.ErrorCorrectionLevel = ErrorCorrectionLevel.L;
            
        

            _qr.Version = QRVersion.Version1;

            _objBitmap = new Bitmap((int)_qr.Version, (int)_qr.Version);
            _objGraphics = Graphics.FromImage(_objBitmap);
            _objGraphics.Clear(Color.Gray);

            colorGray();

            drawPositionDetectionPattern(0, 0);
            drawPositionDetectionPattern(0, ((int)_qr.Version) - 7);
            drawPositionDetectionPattern(((int)_qr.Version) - 7, 0);
            drawFormatInformation();
            drawTimingPattern();


            drawAllAlignmentPatterns(_qr.Version);
            _currentBlockX = ((int)_qr.Version) - 1;
            _currentBlockY = ((int)_qr.Version) - 1;

            // get all used blocks
            for (int col = 0; col < ((int)_qr.Version); col++)
            {
                for (int row = 0; row < ((int)_qr.Version); row++)
                {
                    if (Color.Gray.ToArgb() == _objBitmap.GetPixel(col, row).ToArgb())
                    {
                        Point p = new Point(col, row);
                        _collisionList.Add(p);
                    }
                }
            }

            foreach (byte b in _qr.Bytes)
            {
                while (!drawByte(b)) ;
            }
            _qr.MaskPattern = getMaskPattern();
            _objBitmap.Save("x.png", ImageFormat.Png);
            _objBitmap.Dispose();
            _objGraphics.Dispose();
        }

        private static int getMaskPattern(){
            ArrayList patterns = new ArrayList();
            for (int count = 0; count < 8; count++) {
                patterns.Add(new ArrayList());
            }
            foreach(Point p in _collisionList){
               
            }
            return 0;
        }
        private static Point firstAvailiblePointFromBottom()
        {

            for (int currentIndex = _collisionList.Count - 1; currentIndex > 0; currentIndex--)
            {
                if (Color.Gray.ToArgb() == _objBitmap.GetPixel(((Point)_collisionList[currentIndex]).X, ((Point)_collisionList[currentIndex]).Y).ToArgb())
                {
                    return ((Point)_collisionList[currentIndex]);
                }
            }
            return new Point();
        }
        private static bool drawByte(byte b)
        {
            bool outBool = true;
            string byteString = String.Format("{0:D8}", int.Parse(Convert.ToString(b, 2)));
            Point bottomRight = firstAvailiblePointFromBottom();
            Point topLeft = new Point(bottomRight.X - 1, bottomRight.Y - 3);
            int begin = _collisionList.IndexOf(topLeft);
            int end = _collisionList.IndexOf(bottomRight);
            int charIndex = 0;

            if (!checkBlock(topLeft))
            {
                ArrayList blockList = getBlock(bottomRight);

                for (int count = 0; count < 8; count += 2)
                {
                    _objBitmap.SetPixel(((Point)blockList[count]).X, ((Point)blockList[count]).Y, getPixelColor(int.Parse(byteString[charIndex++].ToString())));
                    _objBitmap.SetPixel(((Point)blockList[count + 1]).X, ((Point)blockList[count + 1]).Y, getPixelColor(int.Parse(byteString[charIndex++].ToString())));
                    _qr.insertModule(new QRModule((Point)blockList[count], true));
                    _qr.insertModule(new QRModule((Point)blockList[count + 1], true));
                }
                _currentBlockX = ((Point)blockList[0]).X;
                _currentBlockY = ((Point)blockList[0]).Y;
            }
            else if (_currentDir == DrawDirection.Upward)
            {
                for (int count = 0; count < 4; count++)
                {
                    _objBitmap.SetPixel(topLeft.X, topLeft.Y + count, getPixelColor(int.Parse(byteString[charIndex++].ToString())));
                    _objBitmap.SetPixel(topLeft.X + 1, topLeft.Y + count, getPixelColor(int.Parse(byteString[charIndex++].ToString())));
                    _qr.insertModule(new QRModule(new Point(topLeft.X, topLeft.Y - count), true));
                    _qr.insertModule(new QRModule(new Point(topLeft.X - 1, topLeft.Y - count), true));

                }
                _currentBlockX = topLeft.X;
                _currentBlockY = topLeft.Y;
            }
            else
            {
                for (int count = 0; count < 4; count++)
                {
                    _objBitmap.SetPixel(topLeft.X, topLeft.Y - count, getPixelColor(int.Parse(byteString[charIndex++].ToString())));
                    _objBitmap.SetPixel(topLeft.X + 1, topLeft.Y - count, getPixelColor(int.Parse(byteString[charIndex++].ToString())));
                    _qr.insertModule(new QRModule(new Point(topLeft.X,topLeft.Y-count),true));
                    _qr.insertModule(new QRModule(new Point(topLeft.X+1,topLeft.Y-count),true));
                }
                _currentBlockX = topLeft.X;
                _currentBlockY = topLeft.Y;
            }
            return outBool;

        }
        private static ArrayList getBlock(Point bottomRight)
        {
            Point currentPoint = bottomRight;
            ArrayList outList = new ArrayList();


            while (outList.Count != 8)
            {
                if (currentPoint.Y <= 0)
                {
                    currentPoint.Y = 0;
                    _currentDir = DrawDirection.Downward;
                    currentPoint.X -= 2;
                }
                else if (currentPoint.Y > 21)
                {
                    currentPoint.Y = 21;
                    _currentDir = DrawDirection.Upward;
                    currentPoint.X -= 2;
                }
                try
                {

                    if (Color.Gray.ToArgb() == _objBitmap.GetPixel(currentPoint.X, currentPoint.Y).ToArgb() || Color.Gray.ToArgb() == _objBitmap.GetPixel(currentPoint.X - 1, currentPoint.Y).ToArgb())
                    {
                        outList.Insert(0, new Point(currentPoint.X, currentPoint.Y));
                        outList.Insert(0, new Point(currentPoint.X - 1, currentPoint.Y));
                    }
                    if (_currentDir == DrawDirection.Upward)
                    {
                        currentPoint = new Point(currentPoint.X, currentPoint.Y - 1);
                    }
                    else
                    {
                        currentPoint = new Point(currentPoint.X, currentPoint.Y + 1);
                    }

                }
                catch
                {
                    if (_currentDir == DrawDirection.Upward)
                    {
                        currentPoint = new Point(currentPoint.X, currentPoint.Y - 1);
                    }
                    else
                    {
                        currentPoint = new Point(currentPoint.X, currentPoint.Y + 1);
                    }
                }
            }
            return outList;

        }
        // ignoring the direction check all current blocks for color
        private static bool checkBlock(Point topLeft)
        {

            for (int count = 0; count < 4; count++)
            {
                try
                {
                    if (Color.Gray.ToArgb() != _objBitmap.GetPixel(topLeft.X, topLeft.Y + count).ToArgb() || Color.Gray.ToArgb() != _objBitmap.GetPixel(topLeft.X + 1, topLeft.Y + count).ToArgb())
                    {
                        return false;
                    }
                }
                catch
                {
                    return false;
                }
            }
            return true;
        }
        private static ArrayList getBlockCoordinatesUp(Point p)
        {
            ArrayList outList = new ArrayList();

            return outList;
        }

        private static ArrayList getBlockCoordinatesDown(int x, int y)
        {
            ArrayList outList = new ArrayList();

            return outList;
        }
        private static int _currentBlockX, _currentBlockY;
        private static DrawDirection _currentDir = DrawDirection.Upward;
        private static bool _printing = true;
        private static Color getPixelColor(int bitVal)
        {
            switch (bitVal)
            {
                case 1: return Color.Black;
                case 2: return Color.White;
                default: return Color.White;

            }
        }
        private static bool editUpwardBlock(int x, int y, Stack input)
        {
            bool printing = true;
            for (int col = x; col > x - 1; col--)
            {
                for (int row = y; row > y - 3; row--)
                {
                    try
                    {
                        if (Color.Gray.ToArgb() != _objBitmap.GetPixel(col, row).ToArgb())
                        {
                            _currentBlockY = row + 1;
                            return false;
                        }
                    }
                    catch
                    {
                        _currentBlockY -= 1;
                        return false;
                    }
                }
            }
            while (printing)
            {
                try
                {
                    if (Color.Gray.ToArgb() == _objBitmap.GetPixel(x, y).ToArgb() && Color.Gray.ToArgb() == _objBitmap.GetPixel(x - 1, y).ToArgb())
                    {
                        _objBitmap.SetPixel(x, y, getPixelColor(int.Parse(input.Pop().ToString())));
                        _objBitmap.SetPixel(x - 1, y, getPixelColor(int.Parse(input.Pop().ToString())));
                    }
                    else
                    {
                        try
                        {
                            input.Pop();
                            input.Pop();
                        }
                        catch { }
                    }
                }
                catch
                {
                    try
                    {
                        input.Pop();
                        input.Pop();
                    }
                    catch { }
                }
                if (input.Count == 0)
                    printing = false;
                y--;
            }
            _currentBlockY = y;
            return true;
        }

        private static bool editDownwardBlock(int x, int y, Stack input)
        {
            for (int col = x; col > x - 1; col--)
            {
                for (int row = y; row > y + 3; row++)
                {
                    try
                    {
                        if (Color.Gray.ToArgb() != _objBitmap.GetPixel(col, row).ToArgb())
                        {
                            _currentBlockY = row - 1;
                            return false;
                        }
                    }
                    catch
                    {
                        _currentBlockY -= 1;
                        return false;
                    }
                }
            }
            bool printing = true;
            while (printing)
            {
                try
                {
                    if (Color.Gray.ToArgb() == _objBitmap.GetPixel(x, y).ToArgb() && Color.Gray.ToArgb() == _objBitmap.GetPixel(x - 1, y).ToArgb())
                    {
                        _objBitmap.SetPixel(x, y, getPixelColor(int.Parse(input.Pop().ToString())));
                        _objBitmap.SetPixel(x - 1, y, getPixelColor(int.Parse(input.Pop().ToString())));
                    }
                    else
                    {
                        try
                        {
                            input.Pop();
                            input.Pop();
                        }
                        catch { }
                    }
                }
                catch
                {
                    try
                    {
                        input.Pop();
                        input.Pop();
                    }
                    catch { }
                }
                if (input.Count == 0)
                    printing = false;
                y++;
            }
            _currentBlockY = y;
            return true;
        }
    }

    public class block
    {
        private int _x;

        public int X
        {
            get { return _x; }
            set { _x = value; }
        }
        private int _y;

        public int Y
        {
            get { return _y; }
            set { _y = value; }
        }
        private bool _value;

        public bool Value
        {
            get { return _value; }
            set { _value = value; }
        }

    }
    public class ByteBlock
    {
        private int _x;

        public int X
        {
            get { return _x; }
            set { _x = value; }
        }
        private int _y;

        public int Y
        {
            get { return _y; }
            set { _y = value; }
        }

        private ArrayList blocks;

        public ArrayList Blocks
        {
            get { return blocks; }
            set { blocks = value; }
        }
        private DrawDirection _direction;

        internal DrawDirection Direction
        {
            get { return _direction; }
            set { _direction = value; }
        }
    }
    enum DrawDirection { Upward, Downward };

}
